package com.globefrog.framework.log;


import com.globefrog.framework.common.constant.CommonConstants;
import com.globefrog.framework.common.constant.LogConstants;
import com.globefrog.framework.log.util.DateExtUtils;
import com.globefrog.framework.log.util.StringExtUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

/**
 * 动作日志实现.
 *
 * @author caijinbang
 * @date 2020-11-04 23:04
 */
@Slf4j
public class ActionLogImpl {

  /**
   * 日志单例对象.
   */
  private static final ActionLogImpl INSTANCE = new ActionLogImpl();
  /**
   * 线程号和动作日志.
   */
  private ConcurrentHashMap<Long, ActionLog> logs = new ConcurrentHashMap<>();

  private ActionLogImpl() {
  }

  /**
   * 获取actionlogImpl实例.
   *
   * @return actionlogImpl实例
   */
  public static ActionLogImpl getInstance() {
    return INSTANCE;
  }

  /**
   * 初始化线程和动作日志.
   */
  public void initialize() {
    // 当前线程id
    long threadId = Thread.currentThread().getId();
    // key=线程id, value=action对象
    logs.put(threadId, new ActionLog());
  }

  /**
   * 录入日志数据.
   *
   * @param key   键
   * @param value 值
   */
  public void logContext(String key, String value) {
    currentActionLog().logContext(key, value);
  }

  /**
   * 获取当前动作日志.
   *
   * @return actionLog
   */
  public ActionLog currentActionLog() {
    // 从缓存中获取当前actionLog
    return logs.get(getTargetThreadId());
  }

  /**
   * 获取目标线程id.
   *
   * @return 目标线程id
   */
  private long getTargetThreadId() {
    // 从MDC获取目标线程id,如果不存在则取当前线程id
    String targetThreadId = MDC.get(LogConstants.MDC_TARGET_THREAD_ID);
    boolean numeric = StringUtils.isNumeric(targetThreadId);
    return numeric ? Long.parseLong(targetThreadId) : Thread.currentThread().getId();
  }

  /**
   * 设置当前action.
   *
   * @param action action
   */
  public void setCurrentAction(String action) {
    this.currentActionLog().setAction(action);
  }

  /**
   * 保存日志.
   */
  public void save() {
    long threadId = Thread.currentThread().getId();
    // 保存日志时需要移除当前线程的缓存数据,所有actionLog打印出来的日志级别都是Info
    ActionLog actionLog = logs.remove(threadId);
    actionLog.setElapsedTime(System.currentTimeMillis() - actionLog.getRequestDate().getTime());
    log.info(buildActionLogText(actionLog));
  }

  /**
   * 构建日志信息.
   *
   * @param actionLog 动作日志
   * @return 日志信息
   */
  private String buildActionLogText(ActionLog actionLog) {
    StringBuilder logInfo = new StringBuilder();
    logInfo.append(DateExtUtils.parse(actionLog.getRequestDate(), LogConstants.LOG_DATA_FORMAT));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(actionLog.getResult());
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(actionLog.getRequestId());
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(actionLog.getAction());
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(actionLog.getElapsedTime());
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(buildLogField(actionLog.getClientIp()));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(buildLogField(actionLog.getRequestUri()));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(buildLogField(actionLog.getHttpMethod()));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(buildLogField(actionLog.getHttpStatusCode()));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(buildLogField(actionLog.getException()));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(removeLineBreak(buildLogField(actionLog.getErrorMessage())));
    logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
    logInfo.append(buildLogField(actionLog.getTraceLogPath()));
    Map<String, String> context = actionLog.getContext();
    if (MapUtils.isNotEmpty(context)) {
      context.forEach((key, value) -> {
        logInfo.append(" " + LogConstants.LOG_SEPARATOR + " ");
        logInfo.append(key);
        logInfo.append(" = ");
        logInfo.append(value);
      });
    }
    return logInfo.toString();
  }

  /**
   * 构建日志字段.
   *
   * @param field 字段
   * @return 字段
   */
  private String buildLogField(Object field) {
    return field == null ? CommonConstants.SPACE : String.valueOf(field);
  }

  /**
   * 移除断行.
   *
   * @param value 数据值
   * @return 移除断行后的值
   */
  private String removeLineBreak(String value) {
    if (StringExtUtil.hasText(value)) {
      String result = value;
      //长度的太长截取,方便action观看
      if (value.length() > LogConstants.LOG_EXCEPTION_LENGTH) {
        result = value.substring(0, LogConstants.LOG_EXCEPTION_LENGTH);
      }
      // 堆栈信息可能存在断行,需要移除
      return result.replaceAll("[\r|\n]]", CommonConstants.SPACE);
    } else {
      return value;
    }
  }
}
